package com.example.java_gobang.api;

import com.example.java_gobang.game.OnlineUserManager;
import com.example.java_gobang.game.RoomManager;
import com.example.java_gobang.mapper.UserMapper;
import com.example.java_gobang.model.GameReadyResponse;
import com.example.java_gobang.model.GameResponse;
import com.example.java_gobang.model.Room;
import com.example.java_gobang.model.User;
import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.ObjectMapper;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import org.springframework.web.socket.CloseStatus;
import org.springframework.web.socket.TextMessage;
import org.springframework.web.socket.WebSocketSession;
import org.springframework.web.socket.handler.TextWebSocketHandler;

import javax.annotation.Resource;
import java.io.IOException;

@Component
public class GameAPI extends TextWebSocketHandler {
    private ObjectMapper objectMapper=new ObjectMapper();

    @Autowired
    private RoomManager roomManager;

    @Autowired
    private OnlineUserManager onlineUserManager;

    @Resource
    private UserMapper userMapper;
    @Override

    //匹配到两个对手加入房间
    public void afterConnectionEstablished(WebSocketSession session) throws Exception {
        GameReadyResponse resp=new GameReadyResponse();

        //1.先获取到用户的身份信息(从HttpSession里拿到了当前用户的对象)
       User user =(User) session.getAttributes().get("user");
       if(user==null){
           resp.setOk(false);
           resp.setReason("用户尚未登录!");
           session.sendMessage(new TextMessage(objectMapper.writeValueAsString(resp)));
           return ;
       }

       //2.判断当前用户是否已经进入房间(拿着房间管理器进行管理)
        Room room=roomManager.getRoomByUserId(user.getUserId());
       if(room==null){
           //如果为空,当前没有找到对应的房间,该玩家还没有匹配到
           resp.setOk(false);
           resp.setReason("当前用户尚未匹配到!");
           session.sendMessage(new TextMessage(objectMapper.writeValueAsString(resp)));
           return;
       }

       //3.判断当前是不是多开(该用户是不是已经在其他地方进入游戏)
        if(onlineUserManager.getFromGameHall(user.getUserId())!=null
        || onlineUserManager.getFromGameRoom(user.getUserId())!=null){
            //如果一个账号,一边在游戏大厅,一边是在游戏房间,也设为多开
            resp.setOk(true);
            resp.setMessage("禁止多开!");
            resp.setMessage("repeatConnection");
            session.sendMessage(new TextMessage(objectMapper.writeValueAsString(resp)));
        }

        //4.设置当前玩家上线!
        onlineUserManager.enterGameRoom(user.getUserId(),session);

        //5.把两个玩家加入到游戏房间中
        //当前这个逻辑是在game_room.html页面加载的时候进行的
        //前面的创建房间/匹配过程是在game_hall.html页面中完成的
        //因此请求匹配到对手之后,需要经过页面跳转,来到game_room.html,才算算正式进入游戏房间(才算玩家准备就绪)
        // 当前这个逻辑是在 game_room.html 页面加载的时候进行的.
        //执行到当前逻辑, 说明玩家已经页面跳转成功了!!
        //页面跳转, 其实是个大活~~ (很有可能出现 "失败" 的情况的)

        //不加锁的话会出现(会出现两个用户争夺玩家1的情况)
        synchronized (room){
            if(room.getUser1()==null){
                //第一个玩家还尚未加入房间
                //就把当前连上的websocket的玩家1作为user,加入到房间中
                room.setUser1(user);
                //把先连入的房间的玩家作为先手方
                room.setWhiteUser(user.getUserId());
                System.out.println("玩家 "+user.getUsername()+" 已经准备就绪!,作为玩家1");
                return;
            }
            if(room.getUser2()==null){
                //说明玩家1已经进入房间,现在要给当前玩家设置为玩家2
                room.setUser2(user);
                System.out.println("玩家 "+user.getUsername()+" 已经准备就绪!,作为玩家2");

                //当两个玩家都加入成功之后,就要让服务器,给两个玩家都返回webSocket响应数据
                //通知这两个玩家说,游戏双方都已经准备好了
                //通知玩家1
                noticeGameReady(room,room.getUser1(),room.getUser2());
                //通知玩家2
                noticeGameReady(room,room.getUser2(),room.getUser1());
                return;
            }
        }


        //6.此处如果又要玩家尝试连接同一个房间,就提示报错
        //这种情况理论上不应该存在,为了让程序更好,做一个判定和提示
        resp.setOk(false);
        resp.setReason("当前房间人数已满,你不能加入房间!");
        session.sendMessage(new TextMessage(objectMapper.writeValueAsString(resp)));
    }

    public void noticeGameReady(Room room, User thisUser, User thatUser) throws IOException {
        GameReadyResponse resp=new GameReadyResponse();
        resp.setMessage("gameReady");
        resp.setOk(true);
        resp.setReason("");
        resp.setRoomId(room.getRoomId());
        resp.setThisUserId(thisUser.getUserId());
        resp.setThatUserId(thatUser.getUserId());
        resp.setWhiteUser(room.getWhiteUser());
        //把当前的响应数据传回给玩家
        WebSocketSession webSocketSession = onlineUserManager.getFromGameRoom(thisUser.getUserId());
        webSocketSession.sendMessage(new TextMessage(objectMapper.writeValueAsString(resp)));
    }

    @Override
    protected void handleTextMessage(WebSocketSession session, TextMessage message) throws Exception {
        //1.先从session里拿到当前的用户对象
        User user=(User)session.getAttributes().get("user");
        if(user==null){
            System.out.println("[handleTextMessage]当前玩家尚未登录!");
            return ;
        }

        //2.根据玩家id获取到房间对象
       Room room= roomManager.getRoomByUserId(user.getUserId());
        System.out.println(user.getUserId());
        //3.通过room对象来处理这次 具体的请求
        room.putChess(message.getPayload());
    }

    @Override
    public void handleTransportError(WebSocketSession session, Throwable exception) throws Exception {
        //退出房间
        User user=(User)session.getAttributes().get("user");
        if(user==null){
            //此处简单处理,在断开连接的时候就给客户端返回响应了
            return;
        }
        WebSocketSession exitSession= onlineUserManager.getFromGameRoom(user.getUserId());
        if(session==exitSession){
            //加上这个判定,目的为了避免在多开的情况下,第二个用户退出连接的动作,导致第一个用户的会话被删除
            onlineUserManager.exitGameRoom(user.getUserId());
        }
        System.out.println("当前用户 "+user.getUsername()+" 游戏房间连接异常");
        //通知对手获胜
        noticeThatUserWin(user);
    }

    private void noticeThatUserWin(User user) throws IOException {
        //根据当前玩家找到玩家所在的房间
       Room room= roomManager.getRoomByUserId(user.getUserId());
       if(room==null){
           //这个情况意味着房间已经被释放了,也没有对手"对手".
           System.out.println("当前房间已经被释放,无需通知对手");
           return;
       }
       //2.根据房间找到对手
        User thatUser=(user== room.getUser1())?room.getUser2():room.getUser1();

       //3.找到对手的在线状态
        WebSocketSession webSocketSession=onlineUserManager.getFromGameRoom(thatUser.getUserId());
        if(webSocketSession==null){
            //这里意味着对手也掉线了!
            System.out.println("对手也已经掉线了,无需通知!");
            return;
        }

        //4.构造一个响应来通知对手,你是获胜方
        GameResponse resp=new GameResponse();
        resp.setMessage("putChess");
        resp.setUserId(thatUser.getUserId());
        resp.setWinner(thatUser.getUserId());
        webSocketSession.sendMessage(new TextMessage(objectMapper.writeValueAsString(resp)));

        //5.更新玩家的分数信息
        int winUserId=thatUser.getUserId();
        int loseUserId=user.getUserId();
        userMapper.userWin(winUserId);
        userMapper.userLose(loseUserId);


        //6.释放房间对象
        roomManager.remove(room.getRoomId(),room.getUser1().getUserId(),room.getUser2().getUserId());
    }

    @Override
    public void afterConnectionClosed(WebSocketSession session, CloseStatus status) throws Exception {
        //退出房间
        User user=(User)session.getAttributes().get("user");
        if(user==null){
            //此处简单处理,在断开连接的时候就给客户端返回响应了
            return;
        }
       WebSocketSession exitSession= onlineUserManager.getFromGameRoom(user.getUserId());
        if(session==exitSession){
            //加上这个判定,目的为了避免在多开的情况下,第二个用户退出连接的动作,导致第一个用户的会话被删除
            onlineUserManager.exitGameRoom(user.getUserId());
        }
        System.out.println("当前用户 "+user.getUsername()+" 离开游戏房间");

        //通知对手获胜
        noticeThatUserWin(user);
    }
}
